#include "sockets_c.h"
#include "../../Mensajes.h"

// Semáforo del thread socket
extern sem_t mutex_socket;

// Semáforo del thread socket
extern sem_t socket_disp;
extern sem_t cliente_conectado;

// Semáforos movimientos
extern sem_t nuevo_mov;
extern sem_t sem_mov;
extern sem_t mov_valido;

// Mensaje inicio
extern Mensaje msj_inicio;

// Flag estado socket servidor (RECV,SEND,INICIO,CONF)
int flag=0;

// Descriptor a operar
extern int socket_op;

// Thread socket en cliente
void* thread_socket_c(void* arg)
{
	int socket,accion = -2;
	cl_config config;
	s_mov* mov = (s_mov*) arg;
		
	// Cargar configuración
	configurar_cliente(&config);
		
	// Iniciar comunicación
	conectar_a_socket(&socket,&config);
	
	// Socket listo para uso
	sem_post(&socket_disp);
	
	while(1)
	{
		// Espero desbloqueo
		lock_socket();
		
		switch(flag)
		{
			case INICIO:
				// Envío nombre de jugador
				if(send(socket,&msj_inicio,sizeof(Mensaje),0) == -1)
				{
					printf("\nSe perdió comunicación con el servidor\n");
					exit(1);
				}
				
			case NUEVO_CONTRINCANTE:
				// Señal inicio partida, primero recibo ACK
				if(recv(socket,mov,sizeof(s_mov),0) <= 0)
				{
					printf("\nSe perdió comunicación con el servidor\n");
					exit(1);
				}
				
				if(mov->estado == ACK)
				{
					if(recv(socket,&msj_inicio,sizeof(Mensaje),0) <= 0)
					{
						printf("\nSe perdió comunicación con el servidor\n");
						exit(1);
					}
				}
				
				// Hay nuevo movimiento
				sem_post(&socket_disp);
				sem_post(&nuevo_mov);
				break;
		
			case RECV:
				// Mutex movimiento
				sem_wait(&sem_mov);
				
				do
				{
					// Espera señal de turno
					if(recv(socket,mov,sizeof(s_mov),0) <= 0)
					{
						printf("\nSe perdió comunicación con el servidor\n");
						exit(1);
					}
				}while(mov->estado<-2 || mov->estado>3 || mov->n_posicion<0 || mov->posicion>9);
			
				// Hay nuevo movimiento
				sem_post(&nuevo_mov);
				sem_post(&sem_mov);
				
				// Thread libre
				sem_post(&socket_disp);
				break;
			
			case SEND:
				// Mutex movimiento
				sem_wait(&sem_mov);
				
				// Envío movimiento al servidor
				if(send(socket,mov,sizeof(s_mov),0) == -1)
				{
					printf("\nSe perdió comunicación con el servidor\n");
					exit(1);
				}
							
				// Espera confirmación
				if(recv(socket,mov,sizeof(s_mov),0) <= 0)
				{
					printf("\nSe perdió comunicación con el servidor\n");
					exit(1);
				}
							
				// Hay nuevo movimiento
				sem_post(&nuevo_mov);
				sem_post(&sem_mov);
				
				// Thread libre
				sem_post(&socket_disp);
				
				break;
		}
	}
}

// Funciones adicionales

// Bloquear socket cliente
void lock_socket()
{
	sem_wait(&mutex_socket);
}

// Desbloquear socket cliente, recibe flag
void unlock_socket(int est)
{
	// Thread ocupado
	sem_wait(&socket_disp);
	
	flag = est;
	sem_post(&mutex_socket);
}

void configurar_cliente(cl_config* arg)
{
	FILE* arch;
	
	if((arch = fopen("config/client_cfg","rt")) == NULL)
	{
		printf("No se puede leer configuración\n");
		exit(1);
	}
	
	fscanf(arch,"IP = %s\nPUERTO = %d",arg->ip_srv,&(arg->puerto));
}

void conectar_a_socket(int* sock_server,cl_config* configuracion)
{
	// Funcion que inicializa el socket
	struct hostent *he;
	struct sockaddr_in server;
	
	if((he = gethostbyname(configuracion->ip_srv)) == NULL)
	{
		printf("Error de host: %s\n",strerror(errno));
		exit(1);
	}

	// Crear socket
	if((*sock_server = socket(AF_INET, SOCK_STREAM, 0))<0)
	{
		printf("Error en socket: %s\n",strerror(errno));
		exit(1);
	}
	
	// Definir propiedades del socket
	server.sin_family = AF_INET;
	server.sin_port = htons(configuracion->puerto);
	server.sin_addr.s_addr = ((struct in_addr *) (he->h_addr))->s_addr;
	bzero(&(server.sin_zero),8);
	
	// Conectar
	if(connect(*sock_server, (struct sockaddr*) &server, sizeof(struct sockaddr_in)) == -1)
	{
		printf("Error en connect: %s\n",strerror(errno));
		close(*sock_server);
		exit(1);
	}	
	
	// Conectado
	sem_post(&cliente_conectado);
}
